/*
 * Copyright 2008-2015 Arsen Chaloyan
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mpf_activity_detector.h"
#include "apt_log.h"
#include "webrtc/common_audio/vad/include/webrtc_vad.h"
#include "apr_file_io.h"
#include "apr_strings.h"

/** Detector states */
typedef enum {
	DETECTOR_STATE_INACTIVITY,           /**< inactivity detected */
	DETECTOR_STATE_ACTIVITY_TRANSITION,  /**< activity detection is in-progress */
	DETECTOR_STATE_ACTIVITY,             /**< activity detected */
	DETECTOR_STATE_INACTIVITY_TRANSITION /**< inactivity detection is in-progress */
} mpf_detector_state_e;

/** Activity detector */
struct mpf_activity_detector_t {
	/* voice activity (silence) level threshold */
	apr_size_t           level_threshold;

	/* period of activity required to complete transition to active state */
	apr_size_t           speech_timeout;
	/* period of inactivity required to complete transition to inactive state */
	apr_size_t           silence_timeout;
	/* noinput timeout */
	apr_size_t           noinput_timeout;

	/* VAD mode for WebRTC VAD (0-3) */
	int16_t              vad_mode;

	/* current state */
	mpf_detector_state_e state;
	/* duration spent in current state  */
	apr_size_t           duration;
	/* frame duration  */
	apr_size_t           frame_duration;
};

/** Load activity detector configuration from file */
static apt_bool_t mpf_activity_detector_config_load(const char *config_file, mpf_activity_detector_t *detector, apr_pool_t *pool)
{
    apr_file_t *file = NULL;
    apr_status_t status;
    char line[1024];
    apt_bool_t result = FALSE;

    /* Initialize default values */
    int16_t vad_mode = 1;
    apr_size_t level_threshold = 2;
    apr_size_t speech_timeout = 200;
    apr_size_t silence_timeout = 200;
    apr_size_t noinput_timeout = 5000;

    /* Try to open configuration file */
    status = apr_file_open(&file, config_file, APR_READ, APR_OS_DEFAULT, pool);
    if (status != APR_SUCCESS) {
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Using default configuration (failed to open: %s)", config_file);
        return FALSE;
    }

    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Loading configuration from: %s", config_file);

    /* Read configuration file line by line */
    while (apr_file_gets(line, sizeof(line), file) == APR_SUCCESS) {
        char *key = line;
        char *value;

        /* Skip empty lines and comments */
        if (line[0] == '#' || line[0] == ';' || line[0] == '\n' || line[0] == '\0') {
            continue;
        }

        /* Remove trailing newline */
        char *newline = strchr(line, '\n');
        if (newline) *newline = '\0';

        /* Find the equals sign */
        value = strchr(line, '=');
        if (!value) {
            continue;
        }

        *value = '\0'; /* Split key and value */
        value++; /* Move past the equals sign */

        /* Trim whitespace from key and value */
        while (*key == ' ' || *key == '\t') key++;
        while (*value == ' ' || *value == '\t') value++;

        char *end = key + strlen(key) - 1;
        while (end > key && (*end == ' ' || *end == '\t' || *end == '\r')) {
            *end = '\0';
            end--;
        }

        end = value + strlen(value) - 1;
        while (end > value && (*end == ' ' || *end == '\t' || *end == '\r')) {
            *end = '\0';
            end--;
        }

        /* Process activity detector configuration parameters */
        if (strcmp(key, "vad_mode") == 0) {
            vad_mode = (int16_t)atoi(value);
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Setting vad_mode: %d", vad_mode);
        }
        else if (strcmp(key, "level_threshold") == 0) {
            level_threshold = (apr_size_t)atoi(value);
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Setting level_threshold: %" APR_SIZE_T_FMT, level_threshold);
        }
        else if (strcmp(key, "speech_timeout") == 0) {
            speech_timeout = (apr_size_t)atoi(value);
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Setting speech_timeout: %" APR_SIZE_T_FMT, speech_timeout);
        }
        else if (strcmp(key, "silence_timeout") == 0) {
            silence_timeout = (apr_size_t)atoi(value);
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Setting silence_timeout: %" APR_SIZE_T_FMT, silence_timeout);
        }
        else if (strcmp(key, "noinput_timeout") == 0) {
            noinput_timeout = (apr_size_t)atoi(value);
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Setting noinput_timeout: %" APR_SIZE_T_FMT, noinput_timeout);
        }
        else {
            apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Activity Detector: Ignoring unknown configuration parameter: %s", key);
        }
    }

    apr_file_close(file);

    /* Apply configuration if valid parameters were found */
    if (vad_mode >= 0 && vad_mode <= 3) {
        /* Store vad_mode in detector for later use in level_calculate */
        detector->vad_mode = vad_mode;
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Applied vad_mode: %d", vad_mode);
    } else {
        detector->vad_mode = 1; /* Default VAD mode */
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Using default vad_mode: %d", detector->vad_mode);
    }

    detector->level_threshold = level_threshold;
    detector->speech_timeout = speech_timeout;
    detector->silence_timeout = silence_timeout;
    detector->noinput_timeout = noinput_timeout;

    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Configuration loaded successfully");
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: level_threshold=%" APR_SIZE_T_FMT ", speech_timeout=%" APR_SIZE_T_FMT ", silence_timeout=%" APR_SIZE_T_FMT ", noinput_timeout=%" APR_SIZE_T_FMT,
           detector->level_threshold, detector->speech_timeout, detector->silence_timeout, detector->noinput_timeout);

    return TRUE;
}

/** Create activity detector */
MPF_DECLARE(mpf_activity_detector_t*) mpf_activity_detector_create(apr_pool_t *pool)
{
	mpf_activity_detector_t *detector = apr_palloc(pool,sizeof(mpf_activity_detector_t));
	detector->level_threshold = 2; /* 0 .. 255 */
	detector->speech_timeout = 200; /* 0.3 s */
	detector->silence_timeout = 200; /* 0.3 s */
	detector->noinput_timeout = 5000; /* 5 s */
	detector->vad_mode = 1; /* Default VAD mode */
	detector->duration = 0;
	detector->frame_duration = CODEC_FRAME_TIME_BASE;
	detector->state = DETECTOR_STATE_INACTIVITY;

    /* Try to load configuration file */
    const char *config_file = "conf/whisper_asr.conf";
    if (!mpf_activity_detector_config_load(config_file, detector, pool)) {
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity Detector: Using default configuration");
    }

	return detector;
}

/** Reset activity detector */
MPF_DECLARE(void) mpf_activity_detector_reset(mpf_activity_detector_t *detector)
{
	detector->duration = 0;
	detector->state = DETECTOR_STATE_INACTIVITY;
}

/** Set threshold of voice activity (silence) level */
MPF_DECLARE(void) mpf_activity_detector_level_set(mpf_activity_detector_t *detector, apr_size_t level_threshold)
{
	detector->level_threshold = level_threshold;
}

/** Set noinput timeout */
MPF_DECLARE(void) mpf_activity_detector_noinput_timeout_set(mpf_activity_detector_t *detector, apr_size_t noinput_timeout)
{
	detector->noinput_timeout = noinput_timeout;
}

/** Set timeout required to trigger speech (transition from inactive to active state) */
MPF_DECLARE(void) mpf_activity_detector_speech_timeout_set(mpf_activity_detector_t *detector, apr_size_t speech_timeout)
{
	detector->speech_timeout = speech_timeout;
}

/** Set timeout required to trigger silence (transition from active to inactive state) */
MPF_DECLARE(void) mpf_activity_detector_silence_timeout_set(mpf_activity_detector_t *detector, apr_size_t silence_timeout)
{
	detector->silence_timeout = silence_timeout;
}

MPF_DECLARE(void) mpf_activity_frame_duration_set(mpf_activity_detector_t *detector, apr_size_t frame_duration)
{
	detector->frame_duration = frame_duration;
}


static APR_INLINE void mpf_activity_detector_state_change(mpf_activity_detector_t *detector, mpf_detector_state_e state)
{
	detector->duration = 0;
	detector->state = state;
    apt_log(MPF_LOG_MARK,APT_PRIO_INFO,"Activity Detector state changed [%d]",state);
}

//static apr_size_t mpf_activity_detector_level_calculate(const mpf_frame_t *frame)
//{
//	apr_size_t sum = 0;
//	apr_size_t count = frame->codec_frame.size/2;
//	const apr_int16_t *cur = frame->codec_frame.buffer;
//	const apr_int16_t *end = cur + count;
//
//	for(; cur < end; cur++) {
//		if(*cur < 0) {
//			sum -= *cur;
//		}
//		else {
//			sum += *cur;
//		}
//	}
//
//	return sum / count;
//}

static apr_size_t mpf_activity_detector_level_calculate(const mpf_frame_t *frame, const mpf_activity_detector_t *detector)
{
    // 实际 codec_frame.size = 160
    // 编码帧大小 160 字节， 16位 PCMU 格式，每个样本两个字节，
    apr_size_t samplesCount = frame->codec_frame.size/2;
    // 将音频数据分割成 每 10 ms 一个处理块
    int per_ms_frames = 10;
    // 采样率，每秒采样数
    apr_size_t sampleRate = 8000;
    // 上面一个处理块间隔包含的样本数
    size_t samples = sampleRate * per_ms_frames / 1000;
    if (samples == 0) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "level_calculate 返回-1,samples== 0 ");
        return (apr_size_t)-1;
    }
    // 计算有多少个 per_ms_frames 涉及的块
    size_t nTotal = (samplesCount / samples);
    int16_t *input = frame->codec_frame.buffer;
    VadInst *vadInst;
    if (WebRtcVad_Create(&vadInst)) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "level_calculate 返回-1,WebRtcVad_Init != 0 ");
        return (apr_size_t)-1;
    }
    int status = WebRtcVad_Init(vadInst);
    if (status != 0) {
        WebRtcVad_Free(vadInst);
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "level_calculate 返回-1,WebRtcVad_Init != 0 ");
        return (apr_size_t)-1;
    }
    // 使用配置的 VAD 模式
    // 模式说明：
    // 0 最不敏感， 噪声鲁棒性：容易漏检  ，适用：安静环境
    // 1 中等，    噪声鲁棒性：一般  ，适用：默认
    // 2 中等偏敏， 噪声鲁棒性：适中
    // 3 最敏感， 噪声鲁棒性：嘈杂环境建议用
    int16_t vad_mode = detector->vad_mode;
    apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "WebRtcVad 设置 mode: %d ", vad_mode);
    status = WebRtcVad_set_mode(vadInst, vad_mode);
    if (status != 0) {
        WebRtcVad_Free(vadInst);
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "level_calculate 返回-1,WebRtcVad_set_mode != 0 ");
        return (apr_size_t)-1;
    }
    int cnt = 0;
    int i  = 0;
    if(nTotal > 0) {
        for (i = 0; i < nTotal; i++) {
            //int keep_weight = 0;
            int nVadRet = WebRtcVad_Process(vadInst, sampleRate, input, samples);
            // printf("==========%d=============== \n", nVadRet);
            if (nVadRet == -1) {
                WebRtcVad_Free(vadInst);
                apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "level_calculate,返回-1");
                return (apr_size_t)-1;
            } else {
                if (nVadRet >= 1) {
                    cnt++;
                }
            }
            input += samples;
        }
        WebRtcVad_Free(vadInst);
        if (cnt * 10 < nTotal) {
            apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "level_calculate 返回0,有效帧数量小于总帧10分之一, cnt = %d, nTotal = %d, frame大小= %d", cnt, nTotal, frame->codec_frame.size);
            return 0;
        } else {
            apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "level_calculate 返回1,有效帧数量超过总帧10分之一, cnt = %d, nTotal = %d, frame大小= %d", cnt, nTotal, frame->codec_frame.size);
            return 1;
        }
    }

    if(nTotal == 0) {
        int nVadRet = WebRtcVad_Process(vadInst, sampleRate, input, samplesCount);
        //printf("==========%d=============== \n", nVadRet);
        apt_log(APT_LOG_MARK, APT_PRIO_ERROR, "VAD结果 = %d, frame大小= %d", nVadRet, frame->codec_frame.size);
        WebRtcVad_Free(vadInst);
        return nVadRet;
    }
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "level_calculate 返回0,最终返回, cnt = %d, nTotal = %d, frame大小= %d", cnt, nTotal, frame->codec_frame.size);
    return 0;
}

/** Process current frame */
//MPF_DECLARE(mpf_detector_event_e) mpf_activity_detector_process(mpf_activity_detector_t *detector, const mpf_frame_t *frame)
//{
//	mpf_detector_event_e det_event = MPF_DETECTOR_EVENT_NONE;
//	apr_size_t level = 0;
//	if((frame->type & MEDIA_FRAME_TYPE_AUDIO) == MEDIA_FRAME_TYPE_AUDIO) {
//		/* first, calculate current activity level of processed frame */
//		level = mpf_activity_detector_level_calculate(frame);
//#if 0
//		apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Activity Detector [%"APR_SIZE_T_FMT"]",level);
//#endif
//	}
//
//	if(detector->state == DETECTOR_STATE_INACTIVITY) {
//		if(level >= detector->level_threshold) {
//			/* start to detect activity */
//			mpf_activity_detector_state_change(detector,DETECTOR_STATE_ACTIVITY_TRANSITION);
//		}
//		else {
//			detector->duration += detector->frame_duration;
//			if(detector->duration >= detector->noinput_timeout) {
//				/* detected noinput */
//				det_event = MPF_DETECTOR_EVENT_NOINPUT;
//			}
//		}
//	}
//	else if(detector->state == DETECTOR_STATE_ACTIVITY_TRANSITION) {
//		if(level >= detector->level_threshold) {
//			detector->duration += detector->frame_duration;
//			if(detector->duration >= detector->speech_timeout) {
//				/* finally detected activity */
//				det_event = MPF_DETECTOR_EVENT_ACTIVITY;
//				mpf_activity_detector_state_change(detector,DETECTOR_STATE_ACTIVITY);
//			}
//		}
//		else {
//			/* fallback to inactivity */
//			mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY);
//		}
//	}
//	else if(detector->state == DETECTOR_STATE_ACTIVITY) {
//		if(level >= detector->level_threshold) {
//			detector->duration += detector->frame_duration;
//		}
//		else {
//			/* start to detect inactivity */
//			mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY_TRANSITION);
//		}
//	}
//	else if(detector->state == DETECTOR_STATE_INACTIVITY_TRANSITION) {
//		if(level >= detector->level_threshold) {
//			/* fallback to activity */
//			mpf_activity_detector_state_change(detector,DETECTOR_STATE_ACTIVITY);
//		}
//		else {
//			detector->duration += detector->frame_duration;
//			if(detector->duration >= detector->silence_timeout) {
//				/* detected inactivity */
//				det_event = MPF_DETECTOR_EVENT_INACTIVITY;
//				mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY);
//			}
//		}
//	}
//
//	return det_event;
//}

MPF_DECLARE(mpf_detector_event_e) mpf_activity_detector_process(mpf_activity_detector_t *detector, const mpf_frame_t *frame)
{
    mpf_detector_event_e det_event = MPF_DETECTOR_EVENT_NONE;
    apr_size_t level = 0;
    if((frame->type & MEDIA_FRAME_TYPE_AUDIO) == MEDIA_FRAME_TYPE_AUDIO) {
        level = mpf_activity_detector_level_calculate(frame, detector);
#if 0
        apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Activity Detector --------------------- [%"APR_SIZE_T_FMT"]",level);
#endif
    }

    if(detector->state == DETECTOR_STATE_INACTIVITY) {
        if(level >= 1) {
            apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Activity Detector ----DETECTOR_STATE_ACTIVITY_TRANSITION---------------- [%"APR_SIZE_T_FMT"]",level);
            mpf_activity_detector_state_change(detector, DETECTOR_STATE_ACTIVITY_TRANSITION);
        }
        else {
            detector->duration += CODEC_FRAME_TIME_BASE;
            if(detector->duration >= detector->noinput_timeout) {
                det_event = MPF_DETECTOR_EVENT_NOINPUT;
            }
        }
    }
    else if(detector->state == DETECTOR_STATE_ACTIVITY_TRANSITION) {
        if(level >= 1) {
            detector->duration += CODEC_FRAME_TIME_BASE;
            apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Activity Detector ----DETECTOR_STATE_ACTIVITY-------11111--------- [%"APR_SIZE_T_FMT"]",level);
            if(detector->duration >= detector->speech_timeout) {
                det_event = MPF_DETECTOR_EVENT_ACTIVITY;
                mpf_activity_detector_state_change(detector, DETECTOR_STATE_ACTIVITY);
            }
        }
        else {
            mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY);
        }
    }
    else if(detector->state == DETECTOR_STATE_ACTIVITY) {
        if(level >= 1) {
            apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Activity Detector ----DETECTOR_STATE_ACTIVITY--------2222-------- [%"APR_SIZE_T_FMT"]",level);
            detector->duration += CODEC_FRAME_TIME_BASE;
        } else {
            apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Activity Detector ----DETECTOR_STATE_INACTIVITY_TRANSITION---------------- [%"APR_SIZE_T_FMT"]",level);
            mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY_TRANSITION);
        }
    }
    else if(detector->state == DETECTOR_STATE_INACTIVITY_TRANSITION) {
        if(level >= 1) {
            mpf_activity_detector_state_change(detector,DETECTOR_STATE_ACTIVITY);
        }
        else {
            detector->duration += CODEC_FRAME_TIME_BASE;
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "Activity STATE duration:%d, silence_timeout:%d)", detector->duration, detector->silence_timeout);
            if(detector->duration >= detector->silence_timeout) {
                apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Activity Detector ----DETECTOR_STATE_INACTIVITY---------------- [%"APR_SIZE_T_FMT"]",level);
                det_event = MPF_DETECTOR_EVENT_INACTIVITY;
                mpf_activity_detector_state_change(detector,DETECTOR_STATE_INACTIVITY);
            }
        }
    }
    return det_event;
}